package com.xx.netty.server.handler;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xx.dk.tbsalling.aismessages.AISInputStreamReader;
import com.xx.dk.tbsalling.aismessages.ais.exceptions.UnsupportedMessageType;
import com.xx.dk.tbsalling.aismessages.ais.messages.AISMessage;
import com.xx.dk.tbsalling.aismessages.ais.messages.PositionReport;
import com.xx.dk.tbsalling.aismessages.ais.messages.ShipAndVoyageData;
import com.xx.dk.tbsalling.aismessages.ais.messages.handler.AISMessageHandler;
import com.xx.dk.tbsalling.aismessages.ais.messages.handler.AISMessageHandlerConfig;
import com.xx.dk.tbsalling.aismessages.ais.messages.types.AISMessageType;
import com.xx.netty.server.AisServer;
import com.xx.service.ShipDataService;
import com.xx.system.domain.data.ShipData;
import com.xx.web.domain.entity.ZlShip;
import com.xx.web.mapper.ZlShipMapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.time.LocalDateTime;

/**
 * @description: ais message handler
 * @author: xx
 * @Date 2023/7/14 16:16
 * @version: 1.0
 */
@Slf4j
@ChannelHandler.Sharable
@Component
@RequiredArgsConstructor
public class AisServerHandler extends ChannelInboundHandlerAdapter {

    private final ShipDataService shipDataService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final ZlShipMapper zlShipMapper;
    private static final String REALTIME_SHIP_DATA_KEY = "realtime:ship:data";

    /**
     * 处理接收到的消息
     * 当通道读取到数据时，该方法会被调用
     * 它首先检查缓冲区中是否有可读的字节，并且第一个字节是否为33，这是AIS消息的标识
     * 如果是AIS消息，则创建一个AISInputStreamReader来解析消息，并根据消息类型选择相应的处理程序
     *
     * @param ctx 通道处理上下文
     * @param msg 接收到的消息，预期是一个ByteBuf实例
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){
        ByteBuf buf = (ByteBuf) msg;
        try {
            if (buf.readableBytes() > 0 && buf.getByte(0) == 33) {
                AISInputStreamReader reader = new AISInputStreamReader(
                        new ByteArrayInputStream(ByteBufUtil.getBytes(buf)),
                        message -> {
                            AISMessageType messageType = message.getMessageType();
                            if (messageType != null) {
                                AISMessageHandler handler = AISMessageHandlerConfig.handlers.get(messageType);
                                if (handler != null) {
                                    handler.handle(message);
                                    // 解析AIS消息并保存到数据库
                                    if (message instanceof ShipAndVoyageData || message instanceof PositionReport) {
                                        saveShipData(message);
                                    }
                                } else {
                                    throw new UnsupportedMessageType(messageType.getCode());
                                }
                            }
                        });
                reader.run();
            }
        } finally {
            buf.release();
        }
    }

    private void saveShipData(AISMessage message) {
        ShipData shipData = new ShipData();

        try {
            // 设置MMSI（所有AIS消息都包含MMSI）
            String mmsi = String.valueOf(message.getSourceMmsi().getMMSI());
            shipData.setMmsi(mmsi);

            // 初始化必要字段的默认值
            shipData.setLon(0.0);
            shipData.setLat(0.0);
            shipData.setSog(0f);
            shipData.setCog(0f);
            shipData.setRot(0f);
            shipData.setHeading(0f);

            // 从ZlShip表中查询船舶名称
            ZlShip zlShip = zlShipMapper.selectOne(
                    new LambdaQueryWrapper<ZlShip>()
                            .eq(ZlShip::getMmsi, mmsi)
                            .last("LIMIT 1")
            );

            // 设置船名
            if (zlShip != null && zlShip.getShipNameEn() != null) {
                shipData.setName(zlShip.getShipNameEn());
            } else {
                // 如果在ZlShip表中没有找到，设置临时船名
                shipData.setName("TEMP_" + mmsi);
            }

            // 基础信息设置
            if (message instanceof ShipAndVoyageData) {
                ShipAndVoyageData shipAndVoyageData = (ShipAndVoyageData) message;
                shipData.setCallsign(shipAndVoyageData.getCallsign());
                shipData.setShipType(shipAndVoyageData.getShipType().getCode());
                shipData.setToBow(shipAndVoyageData.getToBow().floatValue());
                shipData.setToStern(shipAndVoyageData.getToStern().floatValue());
                shipData.setToPort(shipAndVoyageData.getToPort().floatValue());
                shipData.setToStarboard(shipAndVoyageData.getToStarboard().floatValue());
                shipData.setDestination(shipAndVoyageData.getDestination());
            }

            // 位置信息设置
            if (message instanceof PositionReport) {
                PositionReport positionReport = (PositionReport) message;
                if (positionReport.getLongitude() != null) {
                    shipData.setLon(positionReport.getLongitude().doubleValue());
                }
                if (positionReport.getLatitude() != null) {
                    shipData.setLat(positionReport.getLatitude().doubleValue());
                }
                shipData.setSog(positionReport.getSpeedOverGround() != null ? positionReport.getSpeedOverGround().floatValue() : 0f);
                shipData.setCog(positionReport.getCourseOverGround() != null ? positionReport.getCourseOverGround().floatValue() : 0f);
                shipData.setRot(positionReport.getRateOfTurn() != null ? positionReport.getRateOfTurn().floatValue() : 0f);
                shipData.setHeading(positionReport.getTrueHeading() != null ? positionReport.getTrueHeading().floatValue() : 0f);
            }

            // 设置时间
            shipData.setTime(message.getMetadata().getReceived().getEpochSecond());
            shipData.setCreateTime(LocalDateTime.now());
            shipData.setUpdateTime(LocalDateTime.now());

            // 保存数据
            shipDataService.saveShipData(shipData);
        } catch (Exception e) {
            log.error("保存船舶数据失败: {}", e.getMessage(), e);
        }
    }


    // 当连接断开时触发 inactive 事件
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        AisServer.clientChannel = null;
        log.debug("{} 已经断开", ctx.channel());
    }

    // 当出现异常时触发
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        AisServer.clientChannel = null;
        log.error("{} 已经异常断开 异常是{}", ctx.channel(), cause.getMessage());
        //cause.printStackTrace();
    }
}
